import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;

public class Logic {

	private ArrayList<Task> tasks = new ArrayList<Task>();
	private AddParser addParser = new AddParser();
	Storage storage;

	// Magic Strings to indicate successful or unsuccessful operation of
	// commands
	private static final String ADD_SUCCESSFUL = "\" %s \"  is added successfully.";
	private static final String ADD_UNSUCCESSFUL = "\" %s \"  is added unsuccessfully.";
	private static final String EDIT_SUCCESSFUL = "Task edited successfully";
	private static final String EDIT_UNSUCCESSFUL = "Task edited unsuccessfully";
	// private static final String DELETE_SUCCESSFUL =
	// "Task deleted successfully";
	// private static final String DELETE_UNSUCCESSFUL =
	// "Task deleted unsuccessfully";
	private static final String DISPLAY_UNSUCCESSFUL = "Display is unsuccessful";
	private static final String INVALID_COMMAND = "Command is invalid";

	// Magic Strings
	private static final String DESIRED_TASK = "%d. %s\n";

	// Magic Strings for different display mode
	private static final String EMPTY_MODE = "empty";
	private static final String PRIORITY_MODE = "!";
	private static final String LABEL_MODE = "-label";

	// private boolean isEditing = false;
	// private int editIndex = -1;

	/*
	 * For V0.1 TESTINGpublic ArrayList<String> tasks = new ArrayList<String>();
	 * tasks.add("Meet Bao Xiao in NUS");tasks.add("Meet Cao Li in NUS");
	 * tasks.add("Meet Keith in NUS");
	 */

	public String processUserInput(String userInput) throws IOException,
			ParseException {
		String output = executeCommand(userInput);

		return output;
	}

	public Logic(Storage s, History h) throws IOException {
		storage = s;
		// tasks = storage.getTasks();
	}

	enum CommandType {
		DISPLAY, ADD, DELETE, EDIT, SAVE, EXIT, INVALID
	};

	public CommandType determineCommandType(String command) {

		if (command == null) {
			throw new Error("Command type string cannot be null!");
		}
		if (command.equalsIgnoreCase("add")) {
			return CommandType.ADD;
		} else if (command.equalsIgnoreCase("display")) {
			return CommandType.DISPLAY;
		} else if (command.equalsIgnoreCase("delete")) {
			return CommandType.DELETE;
		} else if (command.equalsIgnoreCase("edit")) {
			return CommandType.EDIT;
		} else if (command.equalsIgnoreCase("save")) {
			return CommandType.SAVE;
		} else if (command.equalsIgnoreCase("exit")) {
			return CommandType.EXIT;
		} else {
			return CommandType.INVALID;
		}
	}

	public String executeCommand(String userInput) throws IOException,
			ParseException {
		String command = getUserCommand(userInput);
		String data = getUserData(userInput);
		String outputToUser = "";

		CommandType commandType = determineCommandType(command);

		switch (commandType) {
		case ADD:
			outputToUser = add(data);
			break;
		case DISPLAY:
			outputToUser = display(data);
			break;
		case DELETE:
			outputToUser = delete(data);
			break;
		case EDIT:
			outputToUser = edit(data);
			break;
		case SAVE:
			outputToUser = save();
			break;
		case EXIT:
			System.exit(0);
			break;
		case INVALID:
			return INVALID_COMMAND;
		default:
			return "";
		}

		return outputToUser;
	}

	public String getUserCommand(String userInput) {

		String[] words;
		String userCommand;
		String userInputAfterTrim = userInput.trim();

		if (!userInputAfterTrim.equals("")) {
			words = userInput.trim().split(" ");
			userCommand = words[0].trim();
		} else {
			userCommand = "empty";
		}

		return userCommand;

	}

	public String getUserData(String userInput) {

		String[] words;
		String userData;
		String userInputAfterTrim = userInput.trim();

		words = userInputAfterTrim.split(" ", 2);
		if (words.length == 2) {
			userData = words[1].trim();
		} else {
			userData = "empty";
		}
		return userData;
	}

	public String getDisplayMode(String userData) {

		// reuse the code for getUserCommand to get the display type.
		return getUserCommand(userData);
	}

	public int getTaskID(String data) {
		return Integer.valueOf(getUserCommand(data));
	}

	public String getTaskContent(String data) {
		return getUserData(data);
	}

	public String getLabel(String data) {
		return getUserData(data);
	}

	private String save() throws IOException {
		// return storage.writeToStorage(tasks);

		return "WAITING_FOR_STORAGE_MODIFICATION";
	}

	public String edit(String data) throws ParseException {
		int id;
		int taskIndex;
		Task originalTask, modifiedTask;

		id = getTaskID(data);
		if (isIDValid(id)) {
			originalTask = obtainTaskByID(id);
			taskIndex = tasks.indexOf(originalTask);
			data = getTaskContent(data);
			modifiedTask = addParser.parse(data);
			modifiedTask.setId(originalTask.getId());
			tasks.set(taskIndex,modifiedTask);
			return EDIT_SUCCESSFUL;
		} else {
			return EDIT_UNSUCCESSFUL;
		}

		// WAITING FOR STORAGE MODIFICATION

		// save();

	}

	private boolean isIDValid(int id) {
		for (Task task : tasks) {
			if (task.getId() == id) {
				return true;
			}
		}
		return false;
	}

	private Task obtainTaskByID(int id) {
		for (Task task : tasks) {
			if (task.getId() == id) {
				return task;
			}
		}
		return null;
	}

	// Sing Yee
	// This method would remove and return the requested task from the list of
	// tasks and would update the storage
	public String delete(String data) throws IOException {

		int numToDel = -1;
		int numOfTasks = -1;
		String taskToDelete = "";
		numOfTasks = tasks.size();

		String error = "ERROR_INVALID_TASK_NUMBER";

		numToDel = Integer.parseInt(data);
		if (numToDel > 0 && numToDel <= numOfTasks) {
			numToDel--;
			// taskToDelete = tasks.remove(numToDel);

			// WAITING FOR STORAGE MODIFICATION
			// save();
			return taskToDelete;
		} else {
			return error;
		}
	}

	// Sing Yee

	public String display(String data) {

		String allTasks = DISPLAY_UNSUCCESSFUL;
		String displayMode = getDisplayMode(data);

		switch (displayMode) {
		case EMPTY_MODE:
			return displayEmptyMode();
		case PRIORITY_MODE:
			return displayByPriority();
		case LABEL_MODE:
			return displayByLabel(data);
		default:
			return allTasks;
		}
	}

	private String displayEmptyMode() {
		String allTasks = "NO_TASKS";
		StringBuilder obtainAllTasks = new StringBuilder();

		if (tasks.isEmpty()) {
			return allTasks;
		}
		for (Task task : tasks) {
			obtainAllTasks.append(String.format(DESIRED_TASK, task.getId(),
					task.getContent()));
		}
		allTasks = obtainAllTasks.toString();
		allTasks.trim();
		return allTasks;
	}

	private String displayByPriority() {

		boolean noPrioritisedTask = true;
		String allTasks = DISPLAY_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();

		for (Task task : tasks) {
			if (task.isPriority()) {
				obtainAllTasks.append(String.format(DESIRED_TASK, task.getId(),
						task.getContent()));
				noPrioritisedTask = false;
			}
		}

		if (!noPrioritisedTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			return allTasks;
		} else {
			return allTasks;
		}
	}

	private String displayByLabel(String data) {

		boolean noLabelTask = true;
		String allTasks = DISPLAY_UNSUCCESSFUL;
		StringBuilder obtainAllTasks = new StringBuilder();

		String label = getLabel(data);
		label.trim();

		for (Task task : tasks) {
			if (task.getLabel() == null) {
			} else if (task.getLabel().equals(label)) {
				obtainAllTasks.append(String.format(DESIRED_TASK, task.getId(),
						task.getContent()));
				noLabelTask = false;
			}
		}

		if (!noLabelTask) {
			allTasks = obtainAllTasks.toString();
			allTasks.trim();
			return allTasks;
		} else {
			return allTasks;
		}
	}

	// Bao Xiao
	public String add(String data) throws IOException, ParseException {

		Task newTask = addParser.parse(data);

		if (newTask == null) {
			return String.format(ADD_UNSUCCESSFUL, data);
		} else {
			tasks.add(newTask);

			// WAITING FOR STORAGE MODIFICATION
			// save();
			return String.format(ADD_SUCCESSFUL, newTask.getUserInput());
		}

	}

}
